#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/cpu.h>
#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/sched/init.h>
#include <linux/stdlib.h>
#include <linux/irqflags.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/signal.h>
#include <linux/topology.h>

struct kthread
{
    struct task_struct *task;
    struct completion parked;
    struct completion exited;
    int started;
    int result;
    int cpu;
};

struct kthread_create_info
{
    struct list_head list;

    /* Result passed back to kthread_create() from kthreadd. */
    void *result;
    struct completion *done;

    /* Information passed to kthread() from kthreadd. */
    int (*threadfn)(void *data);
    void *data;
    char full_name[16];
    int node;
};

static DEFINE_SPINLOCK(kthread_create_lock);
static LIST_HEAD(kthread_create_list);
static DECLARE_COMPLETION(kthread_dowork);

static void __kthread_parkme(struct task_struct *k);

static inline struct kthread *to_kthread(struct task_struct *k)
{
    WARN_ON(!(k->flags & PF_KTHREAD));
    return k->worker_private;
}

static void set_kthread_struct(struct task_struct *p, struct kthread *kthread)
{
    init_completion(&kthread->exited);
    init_completion(&kthread->parked);

    kthread->task = p;
    kthread->started = 1;

    p->worker_private = kthread;
}

static int kthread_entry(void *_create)
{
    struct kthread_create_info *create = _create;
    int (*threadfn)(void *data) = create->threadfn;
    void *data = create->data;
    struct kthread self;
    int ret;

    set_kthread_struct(current, &self);

	/* OK, tell user we're spawned, wait for stop or wakeup */
	__set_current_state(TASK_UNINTERRUPTIBLE);

    create->result = self.task;

	/*
	 * Thread is going to call schedule(), do not preempt it,
	 * or the creator may spend more time in wait_task_inactive().
	 */
	preempt_disable();
    complete(create->done);
	schedule_preempt_disabled();
	preempt_enable();

    if (!self.task->kthread.should_stop)
    {
        __kthread_parkme(self.task);
        ret = threadfn(data);
    }
    else
    {
        ret = -EINTR;
    }

    kthread_exit(ret);

    return 0;
}

static void create_kthread(struct kthread_create_info *create)
{
    int pid;

    pid = kernel_thread(kthread_entry, create, create->full_name,
                        CLONE_FS | CLONE_FILES | SIGCHLD);
    if (pid < 0)
    {
        create->result = ERR_PTR(pid);
        complete(create->done);
    }
}

static bool do_create_one(void)
{
    struct kthread_create_info *create = NULL;

    spin_lock(&kthread_create_lock);
    if (!list_empty(&kthread_create_list))
    {

        create = list_first_entry(&kthread_create_list, struct kthread_create_info, list);
        list_del_init(&create->list);
    }
    spin_unlock(&kthread_create_lock);

    if (create)
    {
        create_kthread(create);
    }

    return (create != NULL);
}

static int kthreadd(void *unused)
{
    while (1)
    {
        wait_for_completion(&kthread_dowork);

        do
        {
        } while (do_create_one());
    }

    return 0;
}

static struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
                                                    void *data, int node,
                                                    const char namefmt[],
                                                    va_list args)
{
    DECLARE_COMPLETION(done);
    struct kthread_create_info create;
    int len;

    create.threadfn = threadfn;
    create.data = data;
    create.node = node;
    create.done = &done;
    len = vsnprintf(create.full_name, sizeof(create.full_name) - 1, namefmt, args);
    create.full_name[len] = 0;

    spin_lock(&kthread_create_lock);
    list_add_tail(&create.list, &kthread_create_list);
    spin_unlock(&kthread_create_lock);
    complete(&kthread_dowork);

    wait_for_completion(&done);

    return create.result;
}

static void __kthread_bind_mask(struct task_struct *p, const struct cpumask *mask, unsigned int state)
{
    unsigned long flags;

    if (!wait_task_inactive(p, state))
    {
        WARN_ON(1);
        return;
    }

    /* It's safe because the task is inactive. */
    raw_spin_lock_irqsave(&p->pi_lock, flags);
    do_set_cpus_allowed(p, mask);
    p->flags |= PF_NO_SETAFFINITY;
    raw_spin_unlock_irqrestore(&p->pi_lock, flags);
}

static void __kthread_bind(struct task_struct *p, unsigned int cpu, unsigned int state)
{
    __kthread_bind_mask(p, cpumask_of(cpu), state);
}

static void __kthread_parkme(struct task_struct *k)
{
    struct kthread *self = to_kthread(k);

    for (;;)
    {
        /*
         * TASK_PARKED is a special state; we must serialize against
         * possible pending wakeups to avoid store-store collisions on
         * task->state.
         *
         * Such a collision might possibly result in the task state
         * changin from TASK_PARKED and us failing the
         * wait_task_inactive() in kthread_park().
         */
        __set_special_state(k, TASK_PARKED);
        if (!k->kthread.should_park)
            break;

        /*
         * Thread is going to call schedule(), do not preempt it,
         * or the caller of kthread_park() may spend more time in
         * wait_task_inactive().
         */
        preempt_disable();
        complete(&self->parked);
        schedule_preempt_disabled();
        preempt_enable();
    }
    __set_current_state(TASK_RUNNING);
}

int kthread_daemon_startup(void)
{
    return kernel_thread(kthreadd, NULL, "kthreadd", CLONE_FS | CLONE_FILES);
}

void kthread_parkme(void)
{
    __kthread_parkme(current);
}

void kthread_exit(long result)
{
    do_exit(0);
}

struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
                                           void *data, int node,
                                           const char namefmt[], ...)
{
    struct task_struct *task;
    va_list args;

    va_start(args, namefmt);
    task = __kthread_create_on_node(threadfn, data, node, namefmt, args);
    va_end(args);

    return task;
}

/**
 * kthread_stop - stop a thread created by kthread_create().
 * @k: thread created by kthread_create().
 *
 * Sets kthread_should_stop() for @k to return true, wakes it, and
 * waits for it to exit. This can also be called after kthread_create()
 * instead of calling wake_up_process(): the thread will exit without
 * calling threadfn().
 *
 * If threadfn() may call kthread_exit() itself, the caller must ensure
 * task_struct can't go away.
 *
 * Returns the result of threadfn(), or %-EINTR if wake_up_process()
 * was never called.
 */
int kthread_stop(struct task_struct *k)
{
    struct kthread *kthread;
    int ret;

    get_task_struct(k);
    kthread = to_kthread(k);
    k->kthread.should_stop = 1;
    kthread_unpark(k);
    set_tsk_thread_flag(k, TIF_NOTIFY_SIGNAL);
    wake_up_process(k);
    wait_for_completion(&kthread->exited);
    ret = kthread->result;
    put_task_struct(k);

    return ret;
}

/**
 * kthread_bind - bind a just-created kthread to a cpu.
 * @p: thread created by kthread_create().
 * @cpu: cpu (might not be online, must be possible) for @k to run on.
 *
 * Description: This function is equivalent to set_cpus_allowed(),
 * except that @cpu doesn't need to be online, and the thread must be
 * stopped (i.e., just returned from kthread_create()).
 */
void kthread_bind(struct task_struct *p, unsigned int cpu)
{
    __kthread_bind(p, cpu, TASK_UNINTERRUPTIBLE);
}

/**
 * kthread_create_on_cpu - Create a cpu bound kthread
 * @threadfn: the function to run until signal_pending(current).
 * @data: data ptr for @threadfn.
 * @cpu: The cpu on which the thread should be bound,
 * @namefmt: printf-style name for the thread. Format is restricted
 *	     to "name.*%u". Code fills in cpu number.
 *
 * Description: This helper function creates and names a kernel thread
 */
struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
                                          void *data, unsigned int cpu,
                                          const char *namefmt)
{
    struct task_struct *p;

    p = kthread_create_on_node(threadfn, data, cpu_to_node(cpu), namefmt,
                               cpu);
    if (!IS_ERR(p))
    {
        kthread_bind(p, cpu);
        /* CPU hotplug need to bind once again when unparking the thread. */
        to_kthread(p)->cpu = cpu;
    }

    return p;
}

/**
 * kthread_unpark - unpark a thread created by kthread_create().
 * @k:		thread created by kthread_create().
 *
 * Sets kthread_should_park() for @k to return false, wakes it, and
 * waits for it to return. If the thread is marked percpu then its
 * bound to the cpu again.
 */
void kthread_unpark(struct task_struct *k)
{
    if (!k->kthread.should_park)
        return;
    /*
     * Newly created kthread was parked when the CPU was offline.
     * The binding was lost and we need to set it again.
     */
    if (k->kthread.is_percpu)
        __kthread_bind(k, to_kthread(k)->cpu, TASK_PARKED);

    k->kthread.should_park = 0;
    /*
     * __kthread_parkme() will either see !SHOULD_PARK or get the wakeup.
     */
    wake_up_state(k, TASK_PARKED);
}

/**
 * kthread_park - park a thread created by kthread_create().
 * @k: thread created by kthread_create().
 *
 * Sets kthread_should_park() for @k to return true, wakes it, and
 * waits for it to return. This can also be called after kthread_create()
 * instead of calling wake_up_process(): the thread will park without
 * calling threadfn().
 *
 * Returns 0 if the thread is parked, -ENOSYS if the thread exited.
 * If called by the kthread itself just the park bit is set.
 */
int kthread_park(struct task_struct *k)
{
    struct kthread *kthread = to_kthread(k);

    if (WARN_ON(k->flags & PF_EXITING))
        return -ENOSYS;

    if (WARN_ON_ONCE(k->kthread.should_park))
        return -EBUSY;

    k->kthread.should_park = 1;
    if (k != current)
    {
        wake_up_process(k);
        /*
         * Wait for __kthread_parkme() to complete(), this means we
         * _will_ have TASK_PARKED and are about to call schedule().
         */
        wait_for_completion(&kthread->parked);
        /*
         * Now wait for that schedule() to complete and the task to
         * get scheduled out.
         */
        WARN_ON_ONCE(!wait_task_inactive(k, TASK_PARKED));
    }

    return 0;
}

void kthread_set_per_cpu(struct task_struct *k, int cpu)
{
    struct kthread *kthread = to_kthread(k);

    if (!kthread)
        return;

    WARN_ON_ONCE(!(k->flags & PF_NO_SETAFFINITY));

    if (cpu < 0)
    {
        k->kthread.is_percpu = 0;
        return;
    }

    kthread->cpu = cpu;
    k->kthread.is_percpu = 1;
}

/**
 * kthread_stop_put - stop a thread and put its task struct
 * @k: thread created by kthread_create().
 *
 * Stops a thread created by kthread_create() and put its task_struct.
 * Only use when holding an extra task struct reference obtained by
 * calling get_task_struct().
 */
int kthread_stop_put(struct task_struct *k)
{
    int ret;

    ret = kthread_stop(k);
    put_task_struct(k);

    return ret;
}

/**
 * kthread_should_park - should this kthread park now?
 *
 * When someone calls kthread_park() on your kthread, it will be woken
 * and this will return true.  You should then do the necessary
 * cleanup and call kthread_parkme()
 *
 * Similar to kthread_should_stop(), but this keeps the thread alive
 * and in a park position. kthread_unpark() "restarts" the thread and
 * calls the thread function again.
 */
bool kthread_should_park(void)
{
    struct task_struct *k = current;

    return k->kthread.should_park;
}

/**
 * kthread_should_stop - should this kthread return now?
 *
 * When someone calls kthread_stop() on your kthread, it will be woken
 * and this will return true.  You should then return, and your return
 * value will be passed through to kthread_stop().
 */
bool kthread_should_stop(void)
{
    struct task_struct *k = current;

    return k->kthread.should_stop;
}
